﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace JustCleanIt.DataStructure
{
    public static class Utility
    {
        /// <summary>
        /// Regular expression, which is used to validate an E-Mail address.
        /// </summary>
        private const string MATCH_EMAIL_PATTERN =
                  @"^(([\w-]+\.)+[\w-]+|([a-zA-Z]{1}|[\w-]{2,}))@"
                + @"((([0-1]?[0-9]{1,2}|25[0-5]|2[0-4][0-9])\.([0-1]?
                  [0-9]{1,2}|25[0-5]|2[0-4][0-9])\."
                + @"([0-1]?[0-9]{1,2}|25[0-5]|2[0-4][0-9])\.([0-1]?
                  [0-9]{1,2}|25[0-5]|2[0-4][0-9])){1}|"
                + @"([a-zA-Z]+[\w-]+\.)+[a-zA-Z]{2,4})$";

        private const string MATCH_PIN_CODE_PATTERN = @"^\d{6}$";
        private const string MATCH_PHONE_PATTERN = @"^[+91]{3}\d{10}$";
        private const int PASSWORD_MINIMUM_LENGTH = 8;

        private static string myAllowedSpecialChars = "@#_";

        /// <summary>
        /// Checks whether the given Email-Parameter is a valid E-Mail address.
        /// </summary>
        /// <param name="email">Parameter-string that contains an E-Mail address.</param>
        /// <returns>True, when Parameter-string is not null and 
        /// contains a valid E-Mail address;
        /// otherwise false.</returns>
        public static bool IsValidEmailFormat(string email)
        {
            if (email != null)
            {
                return Regex.IsMatch(email, MATCH_EMAIL_PATTERN);
            }
            else
            {
                return false;
            }
        }

        public static Address GetAddress(string pincode, string nickID, bool isDefault, string street, string landmark, string city, string state, string country)
        {
            Address address = new Address();
            address.Pincode = pincode;
            address.NickID = nickID;
            address.IsDefault = isDefault;
            address.Street = street;
            address.LandMark = landmark;
            address.City = city;
            address.State = state;
            address.Country = country;

            return address;
        }

        public static Customer GetCustomer(string firstName, string lastName, string email, string phone, Gender gender, Address address)
        {
            Customer customer = new Customer();
            customer.FirstName = firstName;
            customer.LastName = lastName;
            customer.Email = email;
            customer.Phone = phone;
            customer.Gender = gender;
            customer.Address = new Address[] { address };

            return customer;
        }

        public static string ValidateNewCustomerDetails(List<Customer> customers, Customer customer, string password2)
        {
            string result = string.Empty;
            string exactPassword = password2.Substring(3);

            if (string.IsNullOrWhiteSpace(customer.FirstName))
            {
                result = "First Name cannot be empty!";
            }
            else if (string.IsNullOrWhiteSpace(customer.LastName))
            {
                result = "Last Name cannot be empty!";
            }
            else if (string.IsNullOrWhiteSpace(customer.Email))
            {
                result = "Email cannot be empty!";
            }
            else if (!IsValidEmailFormat(customer.Email))
            {
                result = "Email format is incorrect!";
            }
            else if (customers.Exists(e => string.Compare(e.Email, customer.Email, StringComparison.OrdinalIgnoreCase) == 0))
            {
                result = "Customer already registered. <a href=www.google.com>Forgot Password</a>";
            }
            else if (string.IsNullOrWhiteSpace(customer.Password))
            {
                result = "Password cannot be empty!";
            }
            else if (!IsPasswordValid(customer.Password, out result))
            {
                // OUT parameter sets result.
            }
            else if (string.IsNullOrWhiteSpace(exactPassword))
            {
                result = "Password mismatch!";
            }
            else if (string.Compare(customer.Password, exactPassword, StringComparison.OrdinalIgnoreCase) != 0)
            {
                result = "Password mismatch!";
            }
            else if (string.IsNullOrWhiteSpace(customer.Phone))
            {
                result = "Phone Number cannot be empty!";
            }
            else if (!Regex.IsMatch(customer.Phone, MATCH_PHONE_PATTERN, RegexOptions.IgnoreCase))
            {
                result = "Phone Number is invalid!";
            }
            else if (customer.Address == null || customer.Address.Length == 0)
            {
                result = "Address cannot be incomplete!";
            }
            else
            {
                result = ValidateNewCustomerAddress(customer.Address[0]);
            }

            return result;
        }

        private static string ValidateNewCustomerAddress(Address address)
        {
            string result = string.Empty;

            if (string.IsNullOrWhiteSpace(address.Street))
            {
                result = "Street Address cannot be empty!";
            }
            else if (string.IsNullOrWhiteSpace(address.LandMark))
            {
                result = "LandMark cannot be empty!";
            }
            else if (string.IsNullOrWhiteSpace(address.State))
            {
                result = "State cannot be empty!";
            }
            else if (string.IsNullOrWhiteSpace(address.Country))
            {
                result = "Country cannot be empty!";
            }
            else if (string.IsNullOrWhiteSpace(address.Pincode))
            {
                result = "Pincode cannot be empty!";
            }
            else if (!Regex.IsMatch(address.Pincode, MATCH_PIN_CODE_PATTERN, RegexOptions.IgnoreCase) || string.Compare(address.Pincode.ToString(), "000000") == 0)
            {
                result = "Pincode is invalid!";
            }

            return result;
        }

        private static List<Rule> rules = new List<Rule>() 
        {
            new Rule(){ Predicate = (s => s != null),
              Description = "Password must not be null" },
            new Rule(){ Predicate = (s => s.Length >= PASSWORD_MINIMUM_LENGTH ),
              Description = "Password must have at least " + PASSWORD_MINIMUM_LENGTH + " characters." },
            new Rule(){ Predicate = (s => s.Count(c => IsSpecialChar(c)) >= 1),
              Description = "Password must contain at least one of " + myAllowedSpecialChars },
            new Rule(){ Predicate = (s => !IsSpecialChar(s[0]) && !IsSpecialChar(s[s.Length - 1])),
              Description = "Password must not start or end with " + myAllowedSpecialChars },
            new Rule(){ Predicate = (s => s.Count(c => Char.IsLetter(c)) > 0),
              Description = "Password must contain at least one letter." },
            new Rule(){ Predicate = (s => s.Count(c => Char.IsDigit(c)) > 0),
              Description = "Password must contain at least one digit." },
            new Rule(){ Predicate = (s =>s.Count(c => !IsValidPasswordChar(c)) == 0),
              Description = "Password must contain letters, digits, or one of " + myAllowedSpecialChars }
        };

        public static bool IsPasswordValid(string password, out string failureReason)
        {
            failureReason = string.Empty;
            foreach (Rule rule in rules)
            {
                if (!rule.Predicate(password))
                {
                    failureReason = rule.Description;
                    return false;
                }
            }
            return true;
        }

        private static bool IsSpecialChar(char c)
        {
            return myAllowedSpecialChars.IndexOf(c) >= 0;
        }

        private static bool IsValidPasswordChar(char c)
        {
            return IsSpecialChar(c) || Char.IsLetterOrDigit(c);
        }
    }

    public class Rule
    {
        public Func<string, bool> Predicate { get; set; }

        public string Description { get; set; }
    }
}
